import { Injectable } from '@angular/core';

import { Subject } from 'rxjs';
import { webSocket, WebSocketSubject } from 'rxjs/webSocket';

import { pb } from 'src/pb';
import { sleep } from './utils';
import { environment } from 'src/environments/environment';
import { SettingsService } from './settings.service';

@Injectable({
  providedIn: 'root'
})
export class WsService {
  write$: Subject<string>;
  private ws: WebSocketSubject<ArrayBuffer>;
  private id: string;
  constructor(
    private ss: SettingsService,
  ) {
    const open$ = new Subject();
    this.ws = webSocket({
      url: environment.ws,
      binaryType: 'arraybuffer',
      serializer: v => v as ArrayBuffer,
      deserializer: v => v.data,
      openObserver: open$,
    });

    open$.subscribe(() => {
      this.next({ type: pb.MsgType.init }, false);
    });

    this.ws.pipe()
      .subscribe(
        msg => this.receiveMessage(msg),
        err => {
          console.error(err);
        },
        () => console.log('complete')
      );
  }

  async open(id: string) {
    if (this.ss.settings.autoRefresh) {
      await this.next({
        interval: this.ss.settings.interval,
        type: pb.MsgType.open,
        id,
      });
    }
  }

  async close(id: string) {
    await this.next({ type: pb.MsgType.close, id });
  }

  private async next(msg: pb.IMsg, hasId = true) {
    if (hasId) {
      while (!this.id) {
        await sleep(100);
      }
    }

    this.ws.next(pb.Msg.encode(pb.Msg.create(msg)).finish());
  }


  private receiveMessage(message: ArrayBuffer) {
    try {
      const buf = new Uint8Array(message as ArrayBuffer);
      const m = pb.Msg.decode(buf);
      switch (m.type) {
        case pb.MsgType.init:
          this.id = m.id;
          break;
        case pb.MsgType.write:
          if (this.ss.settings.autoRefresh) {
            if (this.write$) {
              this.write$.next(m.id);
            }
          }
          break;
      }
    }
    catch (e) {
      console.error(e);
    }
  }
}
